\epi{%
\begin{itemize}
\item{``并行是关于性能的；}
\item{并发是关于程序设计的。''}
\end{itemize}%
}{\textit{Google IO 2010}\\\textsc{ROB PIKE}}
\noindent{}在这章中将展示 Go 使用 channel 和 goroutine 开发并行程序的能力。
goroutine 是 Go 并发能力的核心要素。但是，goroutine 到底 \emph{是}什么？来自
\cite{effective_go}：
\begin{quote}
叫做 goroutine 是因为已有的短语——线程、协程、进程等等——传递了不准确的含义。
goroutine 有简单的模型：\emph{它是与其他 goroutine 并行执行的，有着相同地址空间的函数。}。
它是轻量的，仅比分配栈空间多一点点消耗。而初始时栈是很小的，所以它们也是廉价的，
并且随着需要在堆空间上分配（和释放）。
\end{quote}
\first{goroutine}{goroutine} 是一个普通的函数，只是需要使用关键字
\first{\key{go}}{keyword!go} 作为开头。
\begin{lstlisting}
ready("Tea", 2)	    |\coderemark{普通函数调用}|
go ready("Tea", 2)  |\coderemark{\func{ready()} 作为 goroutine 运行}|
\end{lstlisting}
下面程序的思路来自 \cite{go_course_day3}。
让一个函数作为两个 goroutine 执行，goroutine 等待一段时间，然后打印一些内容到屏幕。
在第 14 和 15 行，启动了 goroutine。
\func{main} 函数等待足够的长的时间，这样每个 goroutine 会打印各自的文本到屏幕。
现在是在第 17 行等待 5 秒钟，
但实际上没有任何办法知道，当所有 goroutine 都已经退出应当等待多久。 
\lstinputlisting[numbers=right,label=src:sleeping,firstnumber=8,caption=Go routine 实践,linerange={8,18}]{src/sleep.go}
表 \ref{src:sleeping} 输出：
\begin{display}
I'm waiting         \coderemark{立刻}
Coffee is ready!    \coderemark{1 秒后}
Tea is ready!       \coderemark{2 秒后}
\end{display}

如果不等待 goroutine 的执行（例如，移除第 17 行），程序立刻终止，而任何正在执行的 
goroutine 都\emph{会停止}。
为了修复这个，需要一些能够同 goroutine 通讯的机制。这一机制通过 \first{channels}{channels} 
的形式使用。\first{channel}{channel} 可以与 Unix sehll 中的双向管道做类比：
可以通过它发送或者接收值。这些值只能是特定的类型：channel 类型。
定义一个 channel 时，也需要定义发送到 channel 的值的类型。注意，必须使用
\key{make} 创建 channel：
\begin{lstlisting}
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
\end{lstlisting}
创建 channel \var{ci} 用于发送和接收整数，创建 channel \var{cs} 用于字符串，
以及 channel \var{cf} 使用了空接口来满足各种类型。
向 channel 发送或接收数据，是通过类似的操作符完成的：
\lstinline{<-}. \index{operator!channel}
具体作用则依赖于操作符的位置：
\begin{lstlisting}
ci <- 1	    |\coderemark{\emph{发送}整数 1 到 channel \var{ci}}|
<-ci	    |\coderemark{从 channel \var{ci} \emph{接收}整数}|
i := <-ci   |\coderemark{从 channel \var{ci} \emph{接收}整数，并保存到 \var{i} 中}|
\end{lstlisting}
将这些放到实例中去。
\begin{lstlisting}[numbers=none,caption=Go routines 和 channel,label=src:sleeping with channels]
var c chan int |\longremark{定义 \var{c} 作为 int 型的 channel。就是说：这个 channel 传输整数。%
注意这个变量是全局的，这样 goroutine 可以访问它；}|

func ready(w string, sec int) {
	time.Sleep(time.Duration(sec) * time.Second)
	fmt.Println(w, "is ready!")
	c <- 1	|\longremark{发送整数 1 到 channel \var{c}；}|
}

func main() {
	c = make(chan int) |\longremark{初始化 \var{c}；}|
	go ready("Tea", 2) |\longremark{用关键字 \key{go} 开始一个 goroutine；}|
	go ready("Coffee", 1)
	fmt.Println("I'm waiting, but not too long")
	<-c |\longremark{等待，直到从 channel 上接收一个值。注意，收到的值被丢弃了；}|
	<-c |\longremark{两个 goroutines，接收两个值。}|
}
\end{lstlisting}

\showremarks
这里仍然有一些丑陋的东西；不得不从 channel 中读取两次（第 14 和 15 行）。
在这个例子中没问题，但是如果不知道有启动了多少个 goroutine 怎么办呢？
这里有另一个 Go 内建的关键字：\first{\key{select}}{keyword!select}。
通过 \key{select}（和其他东西）可以监听 channel 上输入的数据。

在这个程序中使用 \key{select}，并不会让它变得更短，因为运行的 go\-routine 太少了。
移除第 14 和 15 行，并用下面的内容替换它们：
\begin{lstlisting}[caption=使用 select,numbers=right,firstnumber=14]
L: for {
	select {
	case <-c:
		i++ 
		if i > 1 { 
			break L
		}   
	}   
}   
\end{lstlisting}
现在将会一直等待下去。只有当从 channel \var{c} 上收到多个响应时才会退出循环 \var{L}。

\subsection{使其并行运行}
虽然 goroutine 是并发执行的，但是它们并不是并行运行的。如果不告诉 Go 额外的东西，
同一时刻只会有一个 goroutine 执行。利用 \func{runtime.GOMAXPROCS(n)} 可以设置 goroutine 并行执行的数量。
来自文档：
\begin{quote}
GOMAXPROCS 设置了同时运行的 CPU 的最大数量，并返回之前的设置。如果 n < 1，不会改变当前设置。
\emph{当调度得到改进后，这将被移除。}
\end{quote}
如果不希望修改任何源代码，同样可以通过设置环境变量
 \verb|GOMAXPROCS| 为目标值。
%% test
%%\marginpar{%
%%$$\left\{
%%\begin{array}{l}
%%\parbox{2cm}{
%%hallo Yppp hallo Yppp hallo Yppp
%%hallo Yppp hallo Yppp hallo Yppp
%%hallo Yppp
%%}
%%\end{array}
%%\right.$$
%%}

%%\section{So many channels and still \ldots}
\section{更多关于 channel}
\label{sec:more on channels}
当在 Go 中用 \lstinline{ch := make(chan bool)} 创建 chennel 时，bool 型的
\first{无缓冲 channel}{channel!unbuffered} 会被创建。
这对于程序来说意味着什么呢？首先，如果读取（\lstinline{value := <-ch}）它将会被阻塞，直到有数据接收。
其次，任何发送（\lstinline{ch<-5}） 将会被阻塞，直到数据被读出。
无缓冲 channel 是在多个 goroutine 之间同步很棒的工具。
\index{channel!blocking read}
\index{channel!blocking write}

不过 Go 也允许指定 channel 的缓冲大小，很简单，就是 channel 可以存储多少元素。
\lstinline{ch := make(chan bool, 4)}，创建了可以存储 4 个元素的 bool 型 channel。
在这个 channel 中，前 4 个元素可以无阻塞的写入。当写入第 5$^{个}$ 元素时，代码
\emph{将会}阻塞，直到其他 goroutine 从 channel 中读取一些元素，腾出空间。
\index{channel!non-blocking read}
\index{channel!non-blocking write}

一句话说，在 Go 中下面的为 true：
$$
\textrm{\lstinline{ch := make(chan type, value)}}
\left\{
\begin{array}{ll}
value == 0 & \rightarrow \textrm{无缓冲} \\
value >  0 & \rightarrow \textrm{缓冲 \emph{value} 的元素}
\end{array}
\right.
$$

\subsection{关闭 channel}
当 channel 被关闭后，读取端需要知道这个事情。下面的代码演示了如何检查 channel 是否被关系。
\begin{lstlisting}
x, ok = <-ch
\end{lstlisting}
当 \lstinline{ok} 被赋值为 \lstinline{true} 意味着 channel 尚未被关闭，\emph{同时} 可以读取数据。
否则 \var{ok} 被赋值为 \lstinline{false}。在这个情况下表示 channel 被关闭。

\section{练习}
\input{ex-channels/ex-for-channels.tex}

\input{ex-channels/ex-fib.tex}

\cleardoublepage
\section{答案}
\shipoutAnswer
